/**
 * Copyright 2014 Michail Basios
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import java.util.ArrayList;
import java.util.HashMap;

import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.TokenStreamRewriter;

/**
 * This class is used for obtaining information about the objects in the main
 * class of a project
 * 
 * @author <a href="http://www.basios.com" target="blank">Michail Basios</a>
 * @version 1.0 Build February 17, 2014.
 */
/**
 * @author maba18
 * 
 */
public class ExtractObjectsListener extends JavaBaseListener {
	/**
	 * Field Declaration
	 */

	private ArrayList<String> classedToBeChecked = new ArrayList<String>();

	private Parser parser;

	private TokenStream tokenStream;

	private TokenStreamRewriter rewriter;

	private boolean isTable = false;

	private String className = "";

	private Token importToken;

	private String folderPath;

	private HashMap<String, String> generatedClasses = new HashMap<String, String>();
	private HashMap<String, String> relationBetweenDeclarationAndInitialisation = new HashMap<String, String>();

	/**
	 * Stores the objectPathNames and their exact positions in the rewriter
	 * token-stream
	 */
	private HashMap<String, JavaParser.ClassOrInterfaceTypeContext> objectDeclarationInformationMap = new HashMap<String, JavaParser.ClassOrInterfaceTypeContext>();
	private HashMap<String, JavaParser.ClassCreatorRestContext> objectInitialisationInformationMap = new HashMap<String, JavaParser.ClassCreatorRestContext>();

	/**
	 * Class Constructor
	 */
	public ExtractObjectsListener(JavaParser parser) {
		this.parser = parser;
		tokenStream = parser.getTokenStream();
		rewriter = new TokenStreamRewriter(tokenStream);
		/**
		 * Remember that you are taking the global variable from the main class
		 */
		this.folderPath = Main.folderPath;
	}

	// /**
	// * Class Constructor with file Path
	// */
	// public ExtractObjectsListener(JavaParser parser, String folderPath) {
	// this.parser = parser;
	// tokenStream = parser.getTokenStream();
	// rewriter = new TokenStreamRewriter(tokenStream);
	// this.folderPath = folderPath;
	// }

	/**
	 * In case I need to add import statements
	 */
	@Override
	public void enterImportDeclaration(JavaParser.ImportDeclarationContext ctx) {
		importToken = ctx.getStop();
	}

	/**
	 * This methods returns the name of the class
	 */
	@Override
	public void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) {
		className = ctx.Identifier().getText();

	}

	/**
	 * This class is used for obtaining information about the content of the
	 * input Main class
	 */
	@Override
	public void enterClassOrInterfaceType(
			JavaParser.ClassOrInterfaceTypeContext ctx) {
		boolean isTable = false;
		String objectName;

		/**
		 * We check only the arrayList for the moment
		 */
		if (isCollection(ctx.getText())) {
			// System.out.println("ArrayList reeeeeeeeeee: "+ctx.getText());
			// System.out.println(ctx.getParent().getText());
			// System.out.println(ctx.getChildCount());
			// System.out.println(ctx.getChild(1).getChild(1).getText());
			/**
			 * Get the name of the object saved in the collection
			 */
			objectName = tokenStream.get(ctx.start.getTokenIndex() + 2)
					.getText();

			objectName = ctx.getChild(1).getChild(1).getText();
			String objectPathName = folderPath + "/" + objectName + ".java";

			// System.out.println("Object saved in arrayList: " + objectName);

			relationBetweenDeclarationAndInitialisation.put(ctx.getText(),
					objectName);
			classedToBeChecked.add(objectPathName);

			// System.out.println("Object Path name reeee: "+objectPathName);

			objectDeclarationInformationMap.put(objectPathName, ctx);

			/**
			 * Inspects the object and if it is a Table than it generates the
			 * ObjectTabular.java file and also rturn true. By returning true it
			 * helps as making the proper change in the Java main source file
			 */
			try {
				isTable = GenerateTabularArrayList
						.generate(new String[] { objectPathName });
			} catch (Exception e) {
				e.printStackTrace();
			}
			/**
			 * If the object has the annotation @Table, then new specialised
			 * arrayList should be generated
			 */
			if (isTable == true) {

				objectDeclarationInformationMap.remove(objectPathName);

				// System.out.println("EDw paizoume: " + ctx.getText());
				// System.out.println("------------------"
				// + tokenStream.get(ctx.stop.getTokenIndex() + 8)
				// .getText());
				// System.out.println(objectName + "Tabular");
				System.out.println("New class was generated called: "
						+ objectName + "Tabular");
				generatedClasses.put(ctx.getText(), objectName);
				replaceObjectDeclarationInMain(ctx, objectName);
				// rewriter.replace(ctx.getStart(), ctx.getStop(), objectName
				// + "Tabular");
				//
				// // rewriter.insertAfter(importToken, "import " + objectName
				// // + "Tabular;");
				//
				// rewriter.replace(tokenStream.get(ctx.stop.getTokenIndex() +
				// 8),
				// tokenStream.get(ctx.stop.getTokenIndex() + 13),
				// objectName + "Tabular()");
			}

		}
	}

	@Override
	public void enterClassCreatorRest(JavaParser.ClassCreatorRestContext ctx) {
		// System.out.println("Tis manas sassssssssssssssssssssssssssssssssss: "
		// + ctx.getParent().getText());
		String collectionName = ctx.getParent().getChild(0).getText(); // P.x.
																		// arrayList<Demo>

		if (isCollection(collectionName)) {
			// System.out.println("hereeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
			// System.out.println(relationBetweenDeclarationAndInitialisation.get(collectionName));
			objectInitialisationInformationMap.put(
					relationBetweenDeclarationAndInitialisation
							.get(collectionName), ctx);

		}
		if (generatedClasses.containsKey(collectionName)) {
			replaceObjectInitialisationInMain(ctx,
					generatedClasses.get(collectionName));
		}

	}

	private boolean isCollection(String input) {

		if (input.startsWith("ArrayList")) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * This method is used for replacing the collection in the Main java source
	 * file
	 * 
	 * @param ctx
	 * @param objectName
	 */
	public void replaceObjectDeclarationInMain(
			JavaParser.ClassOrInterfaceTypeContext ctx, String objectName) {
		rewriter.replace(ctx.getStart(), ctx.getStop(), objectName + "Tabular");
		// rewriter.replace(tokenStream.get(ctx.stop.getTokenIndex() + 8),
		// tokenStream.get(ctx.stop.getTokenIndex() + 13), objectName
		// + "Tabular()");

		// System.out.println("+++++++++++++++++++++++++++++++++++++++++"
		// + ctx.getText());

	}

	/**
	 * This method is used for replacing the collection in the Main java source
	 * file
	 * 
	 * @param ctx
	 * @param objectName
	 */
	public void replaceAllInMain(JavaParser.ClassOrInterfaceTypeContext ctx,
			String objectName) {

		// System.out.println("----------------------------manas" +
		// ctx.getText());
		// System.out.println(ctx.getParent().getText());
		// System.out.println(objectName);
		rewriter.replace(ctx.getStart(), ctx.getStop(), objectName + "Tabular");
		rewriter.replace(tokenStream.get(ctx.stop.getTokenIndex() + 8),
				tokenStream.get(ctx.stop.getTokenIndex() + 13), objectName
						+ "Tabular();");

	}

	/**
	 * 
	 * @param ctx
	 * @param objectName
	 */
	public void replaceObjectInitialisationInMain(
			JavaParser.ClassCreatorRestContext ctx, String objectName) {
		rewriter.replace(ctx.getParent().getStart(), ctx.getParent().getStop(),
				objectName + "Tabular()");
	}

	/**
	 * Getters and setters
	 * 
	 * @return parser
	 */
	public Parser getParser() {
		return parser;
	}

	public void setParser(Parser parser) {
		this.parser = parser;
	}

	public TokenStream getTokenStream() {
		return tokenStream;
	}

	public void setTokenStream(TokenStream tokenStream) {
		this.tokenStream = tokenStream;
	}

	public TokenStreamRewriter getRewriter() {
		return rewriter;
	}

	public void setRewriter(TokenStreamRewriter rewriter) {
		this.rewriter = rewriter;
	}

	public ArrayList<String> getClassedToBeChecked() {
		return classedToBeChecked;
	}

	public void setClassedToBeChecked(ArrayList<String> classedToBeChecked) {
		this.classedToBeChecked = classedToBeChecked;
	}

	/**
	 * @return the objectInformationMap
	 */
	public HashMap<String, JavaParser.ClassOrInterfaceTypeContext> getObjectDeclarationInformationMap() {
		return objectDeclarationInformationMap;
	}

	/**
	 * @param objectInformationMap
	 *            the objectInformationMap to set
	 */
	public void setObjectDeclarationInformationMap(
			HashMap<String, JavaParser.ClassOrInterfaceTypeContext> objectInformationMap) {
		objectDeclarationInformationMap = objectInformationMap;
	}

	public HashMap<String, JavaParser.ClassCreatorRestContext> getObjectInitialisationInformationMap() {
		return objectInitialisationInformationMap;
	}

	public void setObjectInitialisationInformationMap(
			HashMap<String, JavaParser.ClassCreatorRestContext> objectInitialisationInformationMap) {
		this.objectInitialisationInformationMap = objectInitialisationInformationMap;
	}

}
